/* Copyright (c) 2013 fab-o-lab

 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
 files (the "Software"), to deal in the Software without
 restriction, including without limitation the rights to use,
 copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following
 conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
*/

package com.fabolab.jetset.core;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.StringTokenizer;

import com.fabolab.Archive;
import com.fabolab.Utils;

import com.fabolab.jetset.core.config.model.Context;
import com.fabolab.jetset.core.servlet.ServletException;
import com.fabolab.jetset.core.servlet.ServletOutputStream;
import com.fabolab.jetset.core.servlet.http.HttpServletRequest;
import com.fabolab.jetset.core.servlet.http.HttpServletResponse;
import com.fabolab.jetset.core.wiki.WikiUtils;


/// Simple servlet that tests the Servlet API.
// Sample output:
// <PRE>
// getContentLength(): -1
// getContentType(): null
// getProtocol(): HTTP/1.0
// getScheme(): http
// getServerName(): www.acme.com
// getServerPort(): 1234
// getRemoteAddr(): 192.100.66.1
// getRemoteHost(): acme.com
// getMethod(): GET
// getRequestURI(): http://www.acme.com:1234/TestServlet?foo=bar
// getServletPath(): /TestServlet
// getPathInfo(): null
// getPathTranslated(): null
// getQueryString(): foo=bar
// getRemoteUser(): null
// getAuthType(): null
// 
// Parameters:
//     foo = bar
// 
// Header:
//     accept: text/html, image/gif, image/jpeg, *; q=.2
//     user-agent: Java1.0.2
// </PRE>
// <A HREF="/resources/classes/Acme/Serve/TestServlet.java">Fetch the software.</A><BR>
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>

public class WikiServlet extends com.fabolab.jetset.core.servlet.http.HttpServlet {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	protected int maxSize = 1024 * 1024 * 2; // 2MB

	ResourceBundle resources = null;
	private String pageTitle = null;
	private static int CMD_SAVE = 1;
	private static int CMD_SAVE_MENU = 2;

	private String errMessage = null;

	private Context context = new Context();

	public Context getContext() {
		return context;
	}

	public void setContext(Context context) {
		this.context = context;
	}

	// / Returns a string containing information about the author, version, and
	// copyright of the servlet.
	public String getServletInfo() {
		return "wiki";
	}

	// / Services a single request from the client.
	// @param req the servlet request
	// @param req the servlet response
	// @exception ServletException when an exception has occurred
	public void service(com.fabolab.jetset.core.servlet.http.HttpServletRequest req,
			com.fabolab.jetset.core.servlet.http.HttpServletResponse res)
			throws ServletException, IOException {
		try {
			resources = ResourceBundle.getBundle("fhs");
			this.context.setResources(resources);
			// set file paths
			context.setDocumentPath(resources.getString("fhs.document.url"));
			context.setPublishPath(resources.getString("fhs.publish.url"));
			context.setBackupPath(resources.getString("fhs.backup.url"));
			context.setTemplatePath(resources.getString("fhs.template.url"));
			context.setTemplate(resources.getString("fhs.template.file"));
			context.setPublishTemplate(resources
					.getString("fhs.publish.template.file"));
			context.setCopyright(resources.getString("fhs.copyright"));

		} catch (MissingResourceException mre) {
			log("fhs.properties not found in classpath");
		}
		Enumeration en;
		log("wiki servlet called");
		res.setStatus(HttpServletResponse.SC_OK);
		res.setContentType("text/html");
		
		ServletOutputStream p = res.getOutputStream();
		if (false) {
			p.println("<HTML><HEAD>");
			p.println("<TITLE>Test Servlet Output</TITLE>");
			p.println("<link rel='stylesheet' href='/style/wiki.css'>");
			p.println("</HEAD><BODY>");
			p.println("<H2>Test Servlet Output</H2>");
			p.println("<HR>");
			p.println("<PRE>");
			p.println("getContentLength(): " + req.getContentLength());
			p.println("getContentType(): " + req.getContentType());
			p.println("getProtocol(): " + req.getProtocol());
			p.println("getScheme(): " + req.getScheme());
			p.println("getServerName(): " + req.getServerName());
			p.println("getServerPort(): " + req.getServerPort());
			p.println("getRemoteAddr(): " + req.getRemoteAddr());
			p.println("getRemoteHost(): " + req.getRemoteHost());
			p.println("getMethod(): " + req.getMethod());
			p.println("getRequestURI(): " + req.getRequestURI());
			p.println("getServletPath(): " + req.getServletPath());
			p.println("getPathInfo(): " + req.getPathInfo());
			p.println("getPathTranslated(): " + req.getPathTranslated());
			p.println("getQueryString(): " + req.getQueryString());
			p.println("getRemoteUser(): " + req.getRemoteUser());
			p.println("getAuthType(): " + req.getAuthType());
			p.println("");
			p.println("Parameters:");
			en = req.getParameterNames();
			while (en.hasMoreElements()) {
				String name = (String) en.nextElement();
				p.println("    " + name + " = " + req.getParameter(name));
			}
			p.println("");
			p.println("Headers:");
			en = req.getHeaderNames();
			while (en.hasMoreElements()) {
				String name = (String) en.nextElement();
				p.println("    " + name + ": " + req.getHeader(name));
			}
			p.println("</PRE>");
		}
		boolean headOnly;
		if (req.getMethod().equalsIgnoreCase("get")
				|| req.getMethod().equalsIgnoreCase("post"))
			headOnly = false;
		else if (!req.getMethod().equalsIgnoreCase("head"))
			headOnly = true;
		else {
			res.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
			return;
		}
		String path = req.getServletPath();
		if (path == null || path.charAt(0) != '/') {
			res.sendError(HttpServletResponse.SC_BAD_REQUEST);
			return;
		}
		if (path.indexOf("/../") != -1 || path.endsWith("/..")) {
			res.sendError(HttpServletResponse.SC_FORBIDDEN);
			return;
		}

		// Make a version without the leading /.
		String pathname = path;
		while (pathname.length() > 0 && pathname.charAt(0) == '/')
			pathname = pathname.substring(1);
		if (pathname.length() == 0)
			pathname = "./";

		dispatchPathname(req, res, headOnly, path, pathname, p);

		if (false) {
			p.println("<HR>");
			p.println("</BODY></HTML>");
		}
		p.flush();
		p.close();
	}

	private void dispatchPathname(
			com.fabolab.jetset.core.servlet.http.HttpServletRequest req,
			com.fabolab.jetset.core.servlet.http.HttpServletResponse res,
			boolean headOnly, String path, String pathName,
			ServletOutputStream p) throws IOException {
		String fileName = pathName.replace('/', File.separatorChar);
		if (fileName.charAt(fileName.length() - 1) == File.separatorChar)
			fileName = fileName.substring(0, fileName.length() - 1);
		// fileName = getServletContext().getRealPath(fileName);
		fileName = context.getDocumentPath() + File.separatorChar + fileName;
		log("fileName: " + fileName);
		pageTitle = fileName.substring(
				fileName.lastIndexOf(File.separatorChar) + 1, fileName
						.lastIndexOf("."));
		File file = new File(fileName);
		if (file.exists()) {
			if (!file.isDirectory())
				//check authentication
				if (req.getParameter("cmd") != null) {
					if (req.getParameter("cmd").equalsIgnoreCase("edit") || 
					req.getParameter("cmd").equalsIgnoreCase("save") ||
					req.getParameter("cmd").equalsIgnoreCase("editmenu") ||
					req.getParameter("cmd").equalsIgnoreCase("savemenu") &&
					req.isUserInRole("WIKI"))
					{
						res.sendAuthenticationRequest(HttpServletResponse.SC_UNAUTHORIZED, "WWW-Authenticate", "Basic realm=\"JetSetRealm\"");
					}else{
						serveFile(req, res, headOnly, path, fileName, file, p);
					}
				}else{
					serveFile(req, res, headOnly, path, fileName, file, p);
				}

			else {
				res.sendError(HttpServletResponse.SC_NOT_FOUND);
			}
		} else {
			// File does not exist so create it
			try {
				PrintStream out = new PrintStream(new FileOutputStream(file
						.getPath()));
				out.print("!New page");
				out.close();
				serveFile(req, res, headOnly, path, fileName, file, p);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
			// res.sendError(HttpServletResponse.SC_NOT_FOUND);
		}
	}

	private void serveFile(com.fabolab.jetset.core.servlet.http.HttpServletRequest req,
			com.fabolab.jetset.core.servlet.http.HttpServletResponse res,
			boolean headOnly, String path, String filename, File file,
			ServletOutputStream p) throws IOException {
		log("getting " + file.getPath());
		WikiUtils wki = new WikiUtils();
		if (!file.canRead()) {
			res.sendError(HttpServletResponse.SC_FORBIDDEN);
			return;
		}

		String rangeStr = req.getHeader("Range");
		if (rangeStr != null) {
			// !!!
		}

		if (!headOnly) {
			// read text
			BufferedReader reader = new BufferedReader(new FileReader(file));
			String text = "";
			String line;

			while ((line = reader.readLine()) != null) {
				text += line + "\n";
			}

			reader.close();
			// read menu
			String menuText = "";
			String menuUrl = context.getDocumentPath() + File.separatorChar
					+ "menu.wki";
			reader = new BufferedReader(new FileReader(menuUrl));
			log("getting " + menuUrl);
			menuText = "";

			while ((line = reader.readLine()) != null) {
				menuText += line + "\n";
			}
			reader.close();

			String templateFileName = context.getTemplatePath()
					+ File.separatorChar + context.getTemplate();
			log("getting " + templateFileName);
			if (req.getParameter("cmd") != null) {
				if (req.getParameter("cmd").equalsIgnoreCase("edit")) {
					processEdit(req, p, wki, text, menuText, templateFileName);
				} else if (req.getParameter("cmd").equalsIgnoreCase("save")) {
					processSave(req, file, p, wki, menuText, templateFileName);
				} else if (req.getParameter("cmd").equalsIgnoreCase("editmenu")) {
					processEditMenu(req, p, wki, menuText, templateFileName);
				} else if (req.getParameter("cmd").equalsIgnoreCase("savemenu")) {
					processSaveMenu(req, p, wki, text, templateFileName);
				} else if (req.getParameter("cmd").equalsIgnoreCase("askimage")) {
					processAskImage(req, p, wki, menuText, templateFileName);
				} else if (req.getParameter("cmd").equalsIgnoreCase("listmenu")) {
					processListMenu(req, p);
				} 
			} else {
				if (isMultiPartFormData(req)) {
					processFileUpload(req, p, wki, menuText, templateFileName);
				} else {
					processOther(req, p, wki, text, menuText, templateFileName);
				}
			}
		}
	}

	private void processOther(com.fabolab.jetset.core.servlet.http.HttpServletRequest req,
			ServletOutputStream p, WikiUtils wki, String text, String menuText,
			String templateFileName) throws IOException {
		p.println(composePage(req, wki.getFile(templateFileName), wki
				.parseWiki(text), wki.parseMenu(menuText)));
	}

	private void processFileUpload(
			com.fabolab.jetset.core.servlet.http.HttpServletRequest req,
			ServletOutputStream p, WikiUtils wki, String menuText,
			String templateFileName) throws IOException {
		this.errMessage = uploadFile(req);
		p.println(composePage(req, wki.getFile(templateFileName), errMessage,
				wki.parseMenu(menuText)));
	}

	private void processEdit(com.fabolab.jetset.core.servlet.http.HttpServletRequest req,
			ServletOutputStream p, WikiUtils wki, String text, String menuText,
			String templateFileName) throws IOException {
		p.println(composePage(req, wki.getFile(templateFileName), editWiki(req,
				text, CMD_SAVE), wki.parseMenu(menuText)));
	}

	private void processSave(com.fabolab.jetset.core.servlet.http.HttpServletRequest req,
			File file, ServletOutputStream p, WikiUtils wki, String menuText,
			String templateFileName) throws IOException {
		String text;
		text = Utils.urlDecoder(req.getParameter("source"));
		try {
			PrintStream out = new PrintStream(new FileOutputStream(file
					.getPath()));
			out.print(text);
			out.close();
			BufferedReader reader = new BufferedReader(new FileReader(file));
			text = "";
			String line;

			while ((line = reader.readLine()) != null) {
				text += line + "\n";
			}

			reader.close();			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		processOther(req, p, wki, text, menuText, templateFileName);
	}

	private void processEditMenu(
			com.fabolab.jetset.core.servlet.http.HttpServletRequest req,
			ServletOutputStream p, WikiUtils wki, String menuText,
			String templateFileName) throws IOException {
		p.println(composePage(req, wki.getFile(templateFileName), editWiki(req,
				menuText, CMD_SAVE_MENU), wki.parseMenu(menuText)));
	}

	private void processSaveMenu(
			com.fabolab.jetset.core.servlet.http.HttpServletRequest req,
			ServletOutputStream p, WikiUtils wki, String text,
			String templateFileName) throws IOException {
		String menuText;
		menuText = Utils.urlDecoder(req.getParameter("source"));
		try {
			File menuFile = new File(context.getDocumentPath()
					+ File.separatorChar + "menu.wki");
			PrintStream out = new PrintStream(new FileOutputStream(menuFile
					.getPath()));
			out.print(menuText);
			out.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		processOther(req, p, wki, text, menuText, templateFileName);
	}

	private void processAskImage(
			com.fabolab.jetset.core.servlet.http.HttpServletRequest req,
			ServletOutputStream p, WikiUtils wki, String menuText,
			String templateFileName) throws IOException {
		// TODO replace with resource bundle value
		pageTitle = "Upload image";
		p.println(composePage(req, wki.getFile(templateFileName),
				askImage(req), wki.parseMenu(menuText)));
	}


	
	private void processListMenu(
			com.fabolab.jetset.core.servlet.http.HttpServletRequest req,
			ServletOutputStream p) throws IOException {
		StringBuffer response = new StringBuffer();
		response.append("<div id=\"invertedtabs\">");
		response.append("<ul>");
		StringTokenizer tokens = new StringTokenizer(req
				.getParameter("actions"), ",");
		String table = req.getParameter("table");
		String fields = req.getParameter("fields");
		String action = "";
		String title = "";
		while (tokens.hasMoreTokens()) {
			title = tokens.nextToken();
			if (tokens.hasMoreTokens()) {
				action = tokens.nextToken();
				response.append("<li><a href=\"data.wki?cmd="+ action +"&table="+ table + "&fields=" + fields + "\" title=\""+ title +"\" target=\"_new\"><span>"+ title +"</span></a></li>");
			}
		}
		response.append("</ul>");
		response.append("</div>");
		response.append("<br style=\"clear: left\" />");
		
		p.print(response.toString());
	}
	

	// / Copy a file from in to out.
	// Sub-classes can override this in order to do filtering of some sort.
	public void copyStream(InputStream in, OutputStream out) throws IOException {
		com.fabolab.Utils.copyStream(in, out);
	}

	private String editWiki(com.fabolab.jetset.core.servlet.http.HttpServletRequest req,
			String text, int typeContent) {
		String result = null;
		String command = null;
		if (typeContent == 1) {
			command = "save";
		} else {
			if (typeContent == 2) {
				command = "savemenu";
			} else {
				command = "";
			}
		}
		result = "<span><form action='"
				+ req.getServletPath()
				+ "' method='post'><textarea id='source' name='source' cols='80' rows='20'>"
				+ text
				+ "</textarea><br><input type='hidden' name='cmd' value='"
				+ command + "'><input type='submit' value='OK' /></form>"
				+ "<div><h3>Files</h3>"
				+ getUploadedFilesByPage(this.pageTitle) + "</div></span>";

		return (result);
	}

	private String askImage(com.fabolab.jetset.core.servlet.http.HttpServletRequest req) {
		String result = null;

		result = "<form action='"
				+ req.getServletPath()
				+ "' method='post' enctype='multipart/form-data'><input type='file' name='image' />"
				+ "<br><input type='hidden' name='cmd' value='uploadimage'><input type='submit' value='OK' /></form>";
		return (result);
	}

	private boolean isMultiPartFormData(HttpServletRequest req) {
		boolean result = false;
		if (req.getContentType() != null) {
			if (req.getContentType().toLowerCase().startsWith(
					"multipart/form-data")) {
				result = true;
			}
		}
		return result;
	}

	private String uploadFile(HttpServletRequest req) {
		String strMessage = "Files uploaded!";
		Hashtable table;

		if (req.getContentLength() > maxSize) {
			strMessage = "Content too long max file size is " + maxSize
					+ " bytes.";
			return strMessage;
		}

		int ind = req.getContentType().indexOf("boundary=");
		if (ind == -1) {
			strMessage = "boundary is missing";
			return strMessage;
		}

		String boundary = req.getContentType().substring(ind + 9);

		if (boundary == null) {
			strMessage = "boundary is missing";
			return strMessage;
		}

		try {
			table = parseMulti(boundary, (ServeInputStream) req
					.getInputStream());
		} catch (Throwable t) {
			strMessage = "error parsing form: " + t.getMessage();
			return strMessage;
		}

		byte[] bytes = null;
		String description = null;
		for (Enumeration fields = table.keys(); fields.hasMoreElements();) {
			String name = (String) fields.nextElement();
			Object obj = table.get(name);
			if (obj instanceof Hashtable) {
				// its a file!
				Hashtable filehash = (Hashtable) obj;
				description = (String) filehash.get("filename");
				this.log("File saved: " + description);

				obj = filehash.get("content");
				bytes = (byte[]) obj;
				// System.out.println("Size: " + bytes.length);
				// Save file
				log(context.getDocumentPath() + File.separatorChar + "files"
						+ File.separatorChar + this.pageTitle + "_"
						+ description);
				if (!Utils.writeToFile(context.getDocumentPath()
						+ File.separatorChar + "files" + File.separatorChar
						+ this.pageTitle + "_" + description, bytes, false)) {
					this.log("Error saving file: " + description);
				}
			} else if (obj instanceof String[]) {
				String[] values = (String[]) obj;
				if (name.equals("description")) {
					description = values[0];
				}
			}
		}
		return strMessage;
	}

	private String composePage(HttpServletRequest req, String template,
			String wiki, String menu) {
		// TODO replace template body and menu in template
		String result = "";
		result = template.replaceFirst("�fhs.body�", wiki);
		result = result.replaceFirst("�fhs.menu�", menu);
		result = result.replaceFirst("�fhs.header1�", resources
				.getString("fhs.header1"));
		result = result.replaceFirst("�fhs.header2�", pageTitle);
		result = result.replaceFirst("�fhs.title�", pageTitle);
		result = result.replaceFirst("�fhs.edit�", resources
				.getString("fhs.edit"));
		result = result.replaceFirst("�fhs.imageupload�", resources
				.getString("fhs.imageupload"));
		result = result.replaceFirst("�fhs.editmenu�", resources
				.getString("fhs.editmenu"));
		result = result.replaceFirst("�fhs.edit.url�", req.getServletPath()
				+ "?cmd=edit");
		result = result.replaceFirst("�fhs.editmenu.url�", req.getServletPath()
				+ "?cmd=editmenu");
		result = result.replaceFirst("�fhs.imageupload.url�", req
				.getServletPath()
				+ "?cmd=askimage");
		result = result.replaceFirst("�fhs.prefs�", resources
				.getString("fhs.prefs"));
		result = result.replaceFirst("�fhs.copyright�", resources
				.getString("fhs.copyright"));

		return (result);
	}

	/**
	 * The meat of the servlet. This method parses the input, and returns a
	 * hashtable of either String[] values (for parameters) or Hashtable values
	 * (for files uploaded). The values of the entries in the hashtable are
	 * name, filename, Content-Type, and Contents. Note that uploads should be
	 * capped in size by the calling method, since otherwise a denial of service
	 * attack on server memory becomes trivial.
	 */
	Hashtable parseMulti(String boundary, ServeInputStream in)
			throws IOException {

		int buffSize = 1024 * 64; // 64K
		Hashtable<String, Object> hash = new Hashtable<String, Object>();
		int result;
		String line;
		String lowerline;
		String boundaryStr = "--" + boundary;
		ByteArrayOutputStream content;
		String filename;
		String contentType;
		String name;
		byte[] b = new byte[buffSize];
		boolean isRunning = true;

		result = in.readLine(b, 0, b.length);
		// failure.
		if (result == -1)
			throw new IllegalArgumentException("InputStream truncated");
		line = new String(b, 0, result);
		// line = new String(b,0,0,result);
		// failure.
		if (!line.startsWith(boundaryStr))
			throw new IllegalArgumentException("MIME boundary missing: " + line);

		while (isRunning) {
			// Some initialization
			int size = 0;
			int tel = 0;
			filename = null;
			contentType = null;
			content = new ByteArrayOutputStream();
			name = null;

			// get next line (should be content disposition)
			result = in.readLine(b, 0, b.length);
			if (result == -1)
				return hash;
			line = new String(b, 0, result);
			// line = new String(b,0,result-2);
			lowerline = line.toLowerCase();
			if (!lowerline.startsWith("content-disposition"))
				// don't know what to do, so we'll keep looking...
				continue;
			// determine what the disposition is
			int ind = lowerline.indexOf("content-disposition: ");
			int ind2 = lowerline.indexOf(";");
			if (ind == -1 || ind2 == -1)
				throw new IllegalArgumentException(
						"Content Disposition line misformatted: " + line);
			String disposition = lowerline.substring(ind + 21, ind2);
			if (!disposition.equals("form-data"))
				throw new IllegalArgumentException("Content Disposition of "
						+ disposition + " is not supported");
			// determine what the name is
			int ind3 = lowerline.indexOf("name=\"", ind2);
			int ind4 = lowerline.indexOf("\"", ind3 + 7);
			if (ind3 == -1 || ind4 == -1)
				throw new IllegalArgumentException(
						"Content Disposition line misformatted: " + line);
			name = line.substring(ind3 + 6, ind4);
			// determine filename, if any
			int ind5 = lowerline.indexOf("filename=\"", ind4 + 2);
			int ind6 = lowerline.indexOf("\"", ind5 + 10);
			if (ind5 != -1 && ind6 != -1) {
				filename = line.substring(ind5 + 10, ind6);
			}

			// Whew! We now move onto the next line, which
			// will either be blank, or Content-Type, followed by blank.
			result = in.readLine(b, 0, b.length);
			if (result == -1)
				return hash;
			line = new String(b, 0, result); // -2 to remove \r\n
			// line = new String(b,0,result-2); // -2 to remove \r\n
			lowerline = line.toLowerCase();
			if (lowerline.startsWith("content-type")) {
				int ind7 = lowerline.indexOf(" ");
				if (ind7 == -1)
					throw new IllegalArgumentException(
							"Content-Type line misformatted: " + line);
				contentType = lowerline.substring(ind7 + 1);
				// read blank header line
				result = in.readLine(b, 0, b.length);
				if (result == -1)
					return hash;
				line = new String(b, 0, result); // -2 to remove \r\n
				// line = new String(b,0,0,result-2); // -2 to remove \r\n
				if (line.length() != 0) {
					throw new IllegalArgumentException(
							"Unexpected line in MIMEpart header: " + line);
				}
			} else if (line.length() != 0) {
				throw new IllegalArgumentException(
						"Misformatted line following disposition: " + line);
			}

			// read content, implement readahead by one line
			boolean readingContent = true;
			boolean firstLine = true;
			byte[] buffbytes = new byte[buffSize];
			byte[] crlf = new byte[2];
			crlf[0] = 10;
			int buffnum = 0;

			result = in.readLineRaw(b, 0, b.length);
			tel += 1;
			// System.out.println("size: " + result + " tel: " + tel);

			size = size + result;
			if (result == -1)
				return hash;
			line = new String(b, 0, result);
			// System.out.println(line);
			// line = new String(b,0,0,result);
			if (!line.startsWith(boundaryStr)) {
				System.arraycopy(b, 0, buffbytes, 0, result);
				buffnum = result;
				result = in.readLineRaw(b, 0, b.length);
				tel += 1;
				// System.out.println("size: " + result + " tel: " + tel);

				size = size + result;

				if (result == -1)
					return hash;

				line = new String(b, 0, result);
				// System.out.println(line);
				// line = new String(b,0,0,result);
				firstLine = false;
				if (line.startsWith(boundaryStr)) {
					readingContent = false;
					if (line.endsWith("--") || line.endsWith("--\n")
							|| line.endsWith("--\r\n")) {
						isRunning = false;
					}
				}
			} else {
				readingContent = false;
			}

			while (readingContent) {
				content.write(buffbytes, 0, buffnum);
				// content.write(crlf,0,1);

				System.arraycopy(b, 0, buffbytes, 0, result);
				buffnum = result;
				result = in.readLineRaw(b, 0, b.length);
				tel += 1;
				// System.out.println("size: " + result + " tel: " + tel);
				size = size + result;

				if (result == -1)
					return hash;
				line = new String(b, 0, result);
				// System.out.println(line);
				// line = new String(b,0,0,result);
				if (line.startsWith(boundaryStr)) {
					readingContent = false;
					if (line.endsWith("--") || line.endsWith("--\n")
							|| line.endsWith("--\r\n")) {
						isRunning = false;
					}
				}
			}
			if (!firstLine) {
				// -2 to trim \r\n
				if (buffnum >= 0)
					content.write(buffbytes, 0, buffnum);
				// content.write(crlf,0,1);

				// content.write(buffbytes,0,buffnum-2);
			}

			// System.out.println(">>>total size: " + size);
			// now set appropriate variable, populate hashtable
			if (filename == null) {
				if (hash.get(name) == null) {
					String[] values = new String[1];
					values[0] = content.toString();
					hash.put(name, values);
				} else {
					Object prevobj = hash.get(name);
					if (prevobj instanceof String[]) {
						String[] prev = (String[]) prevobj;
						String[] newStr = new String[prev.length + 1];
						System.arraycopy(prev, 0, newStr, 0, prev.length);
						newStr[prev.length] = content.toString();
						hash.put(name, newStr);
					} else {
						// now what? I think this breaks the standard.
						throw new IllegalArgumentException(
								"failure in parseMulti hashtable building code");
					}
				}
			} else {
				// Yes, we don't return Hashtable[] for multiple files of same
				// name. AFAIK, that's not allowed.
				Hashtable<String, Object> filehash = new Hashtable<String, Object>(
						4);
				filehash.put("name", name);
				filehash.put("filename", filename);
				if (contentType == null)
					contentType = "application/octet-stream";
				filehash.put("content-type", contentType);
				filehash.put("content", content.toByteArray());
				hash.put(name, filehash);
			}
		}
		return hash;
	}

	public String getUploadedFilesByPage(String pageTitle) {
		String result = "";
		File dir = new File(context.getDocumentPath() + File.separatorChar
				+ "files");
		if (dir.isDirectory()) {
			String[] entries = dir.list();
			for (int t = 0; t < entries.length; t++) {
				if (entries[t].startsWith(pageTitle + "_")) {
					result += entries[t] + "<br>";
				}
			}
		}
		return result;
	}

}
